﻿using Mina.Filter.Codec.Demux;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mina.Core.Session;
using Mina.Filter.Codec;
using Mina.Core.Buffer;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SharpIM.Http;
using System.Net;
using CSharpApp;

namespace SharpIM
{

    public class HttpRequest
    {
        public HttpRequest(HttpContext httpContext)
        {
            this.HttpContext = httpContext;
        }
        public IPEndPoint LocalEndPoint { get; internal set; }
        public IPEndPoint RemoteEndPoint { get; internal set; }
        public string Method { get; set; }
        public object ResouceUrl { get; internal set; }
        public string Version { get; internal set; }
        public string RawHeader { get; internal set; }
        public string Host { get; set; }
        public string UserAgent { get; set; }
        public string AcceptCharset { get; set; }
        public string AcceptEncoding { get; set; }
        public string AcceptLanguage { get; set; }
        public bool UpgradeInsecureRequests { get; set; }
        KvUnit _queryParameters;
        public KvUnit QueryParameters
        {
            get
            {
                return _queryParameters ?? (_queryParameters = QueryExpander.Expand(this.Query));
            }
        }
        HttpApiRoute _route;
        public HttpApiRoute Route
        {
            get
            {
                return _route ?? (_route = HttpApiRoute.Parse(this));
            }
        }
        Dictionary<string, string> _headers;
        Dictionary<string, string> _cookies;
        public Dictionary<string, string> Headers
        {
            get
            {
                return _headers;
            }
        }
        public Dictionary<string, string> Cookies
        {
            get
            {
                return _cookies;
            }
        }

        internal void ParseHeaders()
        {
            if (_headers == null)
            {
                _cookies = new Dictionary<string, string>();
                _headers = new Dictionary<string, string>();
                if (!string.IsNullOrEmpty(RawHeader))
                {
                    var entities = RawHeader.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (var item in entities)
                    {
                        var pair = item.Split(new string[] { ": " }, 2, StringSplitOptions.None);
                        if (pair.Length == 2 && !_headers.ContainsKey(pair[0]))
                        {
                            _headers.Add(pair[0], pair[1]);
                            if (pair[0] == "Content-Length")
                            {
                                int val;
                                if (int.TryParse(pair[1], out val))
                                {
                                    this.ContentLength = val;
                                }
                            }
                            else if (pair[0] == "Content-Type")
                            {
                                ContentType = pair[1];
                            }
                            else if (pair[0] == "Host")
                            {
                                this.Host = pair[1];
                            }
                            else if (pair[0] == "User-Agent")
                            {
                                this.UserAgent = pair[1];
                            }
                            else if (pair[0] == "Connection")
                            {
                                this.Connection = pair[1];
                            }
                            else if (pair[0] == "Accept-Charset")
                            {
                                this.AcceptCharset = pair[1];
                            }
                            else if (pair[0] == "Accept-Encoding")
                            {
                                this.AcceptEncoding = pair[1];
                            }
                            else if (pair[0] == "Accept-Language")
                            {
                                this.AcceptLanguage = pair[1];
                            }
                            else if (pair[0] == "Upgrade-Insecure-Requests")
                            {
                                this.UpgradeInsecureRequests = pair[1] == "1";
                            }
                            else if (pair[0] == "Cookie")
                            {
                                ParseCookie(pair[1]);
                            }
                            else if (pair[0] == "X-Requested-With")
                            {
                                this.IsAjaxRequest = pair[1] == "XMLHttpRequest";
                            }
                        }
                    }
                }
            }
        }

        void ParseCookie(string cookie)
        {
            if (string.IsNullOrEmpty(cookie))
                return;
            var cookies = cookie.Split(new string[] { "; " }, StringSplitOptions.RemoveEmptyEntries);
            foreach (var item in cookies)
            {
                var pair = item.Split(new char[] { '=' }, 2);
                if (pair.Length == 1)
                {
                    if (pair[0].Length > 0)
                    {
                        _cookies.Remove(pair[0]);
                        _cookies.Add(pair[0], "");
                    }
                }
                else if (pair.Length == 2)
                {
                    if (pair[0].Length > 0)
                    {
                        _cookies.Remove(pair[0]);
                        _cookies.Add(pair[0], "");
                    }
                }
            }
        }
        public string ContentType { get; private set; }
        public int ContentLength { get; private set; }
        public string Connection { get; private set; }
        public bool IsSecure { get; internal set; }

        public HttpRequestParser Parser = new HttpRequestParser();
        public class HttpRequestParser
        {
            public ParserState State = ParserState.HTTP_METHOD;
            public ParserError Error = ParserError.None;
            public int InputBufferPosition { get; internal set; }
        }
        public class ParserReachSizeLimitException : Exception
        {
            public ParserReachSizeLimitException(ParserError error)
            {
                this.Error = error;
            }
            public ParserError Error { get; set; }
        }
        public enum ParserState
        {
            HTTP_METHOD,
            HTTP_RESOUCRE,
            HTTP_VERSION,
            HTTP_ENTITY,
            HTTP_BODY,
            END,
            ERROR
        }
        public enum ParserError
        {
            None,
            HeadIsTooLong,
            BodyIsTooLong,
        }
        public HttpResponse CreateResponse(int statusCode)
        {
            return CreateResponse(statusCode, null as IHttpResult);
        }
        public HttpResponse CreateResponse(int statusCode, IHttpResult body)
        {
            this.HttpContext.Response.StatusCode = statusCode;
            this.HttpContext.Response.Body = body;
            return this.HttpContext.Response;
        }

        public HttpResponse CreateResponse(int statusCode, string statusCodeDescription, IHttpResult body, string contentType)
        {
            this.HttpContext.Response.StatusCode = statusCode;
            this.HttpContext.Response.Body = body;
            this.HttpContext.Response.StatusCodeDescription = statusCodeDescription;
            this.HttpContext.Response.ContentType = contentType;
            return this.HttpContext.Response;
        }

        public HttpResponse CreateRedirectResponse(int statusCode, string location)
        {
            this.HttpContext.Response.StatusCode = statusCode;
            var resp = this.HttpContext.Response;
            resp.SetHeader("Location", location);
            return resp;
        }

        public HttpResponse CreateEmptyResponse(int statusCode = 200)
        {
            return CreateResponse(statusCode, new EmptyResult());
        }

        public HttpResponse CreateFileResponse(string filePath)
        {
            return CreateResponse(200, new FileResult { Path = filePath });
        }
        public HttpResponse CreateFileResponse(string filePath, string contentType)
        {
            return CreateResponse(200, new FileResult { Path = filePath, ContentType = contentType });
        }

        public HttpResponse CreateStreamResponse(Stream stream, string contentType)
        {
            return CreateResponse(200, new StreamResult { Stream = stream, ContentType = contentType });
        }

        public HttpResponse CreateResponse(int statusCode, string body)
        {
            return CreateResponse(statusCode, new StringResult { Value = body });
        }
        public HttpResponse CreateResponse(int statusCode, string body, string contentType)
        {
            return CreateResponse(statusCode, new StringResult { Value = body, ContentType = contentType });
        }

        public HttpResponse CreateResponse(string body, string contentType)
        {
            return CreateResponse(200, new StringResult { Value = body, ContentType = contentType });
        }

        public HttpResponse CreateResponse(string body)
        {
            return CreateResponse(body, "text/plain");
        }

        public HttpResponse CreateJsonResponse(int statusCode, object json)
        {
            return CreateResponse(statusCode, new JsonResult { Data = json, ContentType = "application/json" });
        }

        public HttpResponse CreateJsonResponse(object json)
        {
            return CreateResponse(200, new JsonResult { Data = json, ContentType = "application/json" });
        }

        public HttpResponse CreateJavascriptResponse(string javascriptCode)
        {
            return CreateResponse(200, new JavascriptResult { Javascript = javascriptCode });
        }

        public IRequestPayload Payload { get; set; }
        public object ConstructParameter(string name, Type parameterType)
        {
            if (Payload == null)
            {
                return null;
            }
            else
            {
                return Payload.ReadProperty(name, parameterType);
            }
        }

        public Uri Url
        {
            get
            {
                return new Uri((this.IsSecure ? Uri.UriSchemeHttps : Uri.UriSchemeHttp) + "://" + this.Host + this.ResouceUrl);
            }
        }
        public string Domain
        {
            get
            {
                return Url.Host;
            }
        }
        public int Port
        {
            get
            {
                return Url.Port;
            }
        }

        public string PathAndQuery
        {
            get
            {
                return Url.PathAndQuery;
            }
        }

        public string Query
        {
            get
            {
                return Url.Query;
            }
        }

        public string Scheme
        {
            get
            {
                return Url.Scheme;
            }
        }

        public bool IsLoopback
        {
            get
            {
                return Url.IsLoopback;
            }
        }

        public bool IsAjaxRequest { get; private set; }
        public HttpContext HttpContext { get; private set; }
    }
}
